package yxy.banana.socket.controller.server;

import yxy.apple.protobuf.Constants;
import yxy.apple.protobuf.Message.HeartbeatRequest;
import yxy.apple.protobuf.Message.HeartbeatResponse;
import yxy.apple.protobuf.Message.Parcal;
import yxy.apple.util.scheduler.Schedulable;
import yxy.apple.util.scheduler.ScheduledFuture;
import yxy.apple.util.scheduler.SingleThreadScheduler;
import yxy.banana.socket.NetworkElement;
import yxy.banana.socket.controlled.ControlledHandler;
import yxy.banana.socket.controlled.Controller;

public abstract class ServerHeartbeatController<H extends ControlledHandler> extends Controller<H> {

	protected static final SingleThreadScheduler<Schedulable> scheduler = new SingleThreadScheduler<>();

	private int timeout = 30000;// 30秒超时
	private ScheduledFuture watching = null;

	private class HeartbeatListener implements Schedulable {
		@Override
		public void trigger() {
			ServerHeartbeatController.this.watching = null;
			ServerHeartbeatController.this.silenced();
		}

		@Override
		public void cancelled() {
		}

	}

	public ServerHeartbeatController(int timeout) {
		this.timeout = timeout;
	}

	@Override
	public void open() {
		super.open();
		// XXX 此处开始心跳计时
		// this.watching = ServerHeartbeatController.scheduler.schedule(new
		// HeartbeatListener(), this.timeout);
	}

	@Override
	public void close() {
		if (this.watching != null) {
			this.watching.cancel();
			this.watching = null;
		}
		super.close();
	}

	@Override
	public boolean control(Parcal parcal) throws Exception {
		if (parcal.getProto() == Constants.ControlProto.HeartbeatRequest) {
			try {
				HeartbeatRequest request = HeartbeatRequest.parseFrom(parcal.getData());
				if (this.watching != null) {
					this.watching.renew(this.timeout);
				}
//				System.err.println("收到心跳：：" + request.getTimestamp() + " at " + System.currentTimeMillis());
				this.announced();
				HeartbeatResponse.Builder builder = HeartbeatResponse.newBuilder();
//				builder.setTimestamp(request.getTimestamp());// 回抄
				builder.setTimestamp(System.currentTimeMillis());// 回抄
				HeartbeatResponse response = builder.build();
				long s = NetworkElement.value(this.handler().system(), this.handler().cluster(), this.handler().channel());
				long d = parcal.getSource();
				this.handler().send(parcal.getTag(), s, d, Constants.ControlProto.HeartbeatResponse, response);
			} catch (Exception e) {
				throw e;
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 心跳超时时回调
	 * 
	 * @param handler
	 */
	protected abstract void silenced();

	/**
	 * 有心跳时回调
	 * 
	 * @param handler
	 */
	protected abstract void announced();

}
